/*** ft=ansi_c *****************************************************************
* This file is licensed under [WTFPL v2](https://www.wtfpl.net/).  You may read
* the full license in the file LICENSE or by following the web link.
*
* AUTOTCC - embedding C code as natively as possible inside Lua.
*******************************************************************************/

/***************************************
* configuration
***************************************/

/* select the Lua version here */
#ifndef LUA_VERSION
#define LUA_VERSION _5_4
#endif

/***************************************
* system headers
***************************************/
#include <stdlib.h>

/***************************************
* Lua headers
***************************************/
#if LUA_VERSION == _5_4
#include "lua5.4/lua.h"
#include "lua5.4/lauxlib.h"
#include "lua5.4/lualib.h"
#elif LUA_VERSION == _5_3
#include "lua5.3/lua.h"
#include "lua5.3/lauxlib.h"
#include "lua5.3/lualib.h"
#elif LUA_VERSION == _5_2
#include "lua5.2/lua.h"
#include "lua5.2/lauxlib.h"
#include "lua5.2/lualib.h"
#else
#error LUA_VERSION must be defined.
#endif

/***************************************
* TCC headers
***************************************/
#include "libtcc.h"

/***************************************
* type definitions
***************************************/
typedef TCCState *ud_tcc;
typedef const char ud_type[];
typedef const char *code_type;

/***************************************
* type identifiers for metatables
***************************************/
static const ud_type instance_mt = "__autotcc__";

/***************************************
* module registration functions
***************************************/

/***************************************
* local helper declarations
***************************************/
static inline void assert_type(lua_State *L, int index, const char *name)
{
	luaL_argcheck(L, luaL_checkudata(L, index, name), index, lua_pushfstring(L, "type error: %s expected", name));
}

static ud_tcc *get_tcc_instance(lua_State *);
static code_type read_options(lua_State *);

/***************************************
* API declarations
***************************************/

int luaopen_autotcc(lua_State *);
static int autotcc_instantiate(lua_State *);

/***************************************
* API definitions
***************************************/

/*******************
* register the autotcc library
*******************/
int luaopen_autotcc(lua_State *L)
{
    // create and register instance metatable
    luaL_newmetatable(L, instance_mt);

    // create module table
    lua_newtable(L);

    // create module metatable
    lua_createtable(L, 0, 1);
    lua_pushcfunction(L, autotcc_instantiate);
    lua_setfield(L, -2, "__call");

    // set module metatable
    lua_setmetatable(L, -2);

    // return module table
    return 1;
}

/*******************
* instantiate a compiler, compile, relocate, and bridge
*
* argument is a table with the following keys required:
*
*   [0] = "..."         : string containing code to be compiled
*   defines = { ... }   : array of #define symbols formatted as "<key>" or as { <key> = <value> }
*   exports = { ... }   : array of symbols exported by the code (all strings)
*   ipaths = { ... }    : array of include paths (all strings)
*   libs = { ... }      : array of required external libraries (all strings)
*   lpaths = { ... }    : array of library paths (all strings)
*   options = { ... }   : array of desired compiler options (all strings)
*******************/
static int autotcc_instantiate(lua_State *L)
{
    lua_newtable(L);
    luaL_getmetatable(L, instance_mt);
    lua_setmetatable(L, -2);

    ud_tcc *tcc = get_tcc_instance(L);
    code_type code = read_options(L);
    return 1;


    // Set metatable for new instance

    return 1;  // Return new instance
}

/***************************************
* local helper definitions
***************************************/

/*******************
* create a TCC instance
*******************/
static ud_tcc *get_tcc_instance(lua_State *L)
{
    ud_tcc *tcc = (ud_tcc *)lua_newuserdata(L, sizeof(ud_tcc));
    *tcc = tcc_new();
    if (!*tcc)
    {
        luaL_error(L, "failed to instantiate TCC state");
    }

	tcc_set_output_type(*tcc, TCC_OUTPUT_MEMORY);

    luaL_getmetatable(L, instance_mt);
    lua_setmetatable(L, -2);

	return tcc;
}

/*******************
* extract code and compiler/linker options
*******************/
static code_type read_options(lua_State *L)
{
    UNUSED(L);
    return "";
}

#if 0
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

// Instance metatable name
#define AUTOTCC_INST_MT "__autotcc__"

// Instantiation function
static int autotcc_instantiate(lua_State *L) {
    // Create new instance table
    lua_newtable(L);

    // Get instance metatable from registry
    luaL_getmetatable(L, AUTOTCC_INST_MT);

    // Set metatable for new instance
    lua_setmetatable(L, -2);

    return 1;  // Return new instance
}

#endif

#if 0
#include <libtcc.h>

TCCState* s = tcc_new();  // Create a TCC instance
if (!s) { /* handle error */ }

tcc_set_output_type(s, TCC_OUTPUT_MEMORY);  // Output to memory (JIT)
tcc_add_include_path(s, "/usr/include");    // Add include paths
tcc_define_symbol(s, "MY_APP", "1");        // Define macros

tcc_compile_string(s, "int add(int a, int b) { return a + b; }");

tcc_add_library(s, "m");  // Link math library (-lm)
// Add other libraries as needed

if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0) {
    // Handle relocation error (e.g., symbol resolution failure)
}

int (*user_add)(int, int) = tcc_get_symbol(s, "add");
if (!user_add) { /* handle missing symbol */ }

int result = user_add(2, 3);  // Execute JIT-compiled code

tcc_delete(s);  // Free TCC resources
#endif
